#include "tga.h"
#include <lobi/fm.h>
#include <stdlib.h>
#include <string.h>

/* static helpers */
static int tga_read_header(struct fm_t* fp, struct tga_header_t* tga_header);
static int tga_read_image_data(struct fm_t* fp, struct tga_header_t* tga_header, struct image_t* image, int want_rgba);
static int tga_get_packet(int* current_byte,struct tga_header_t* tga_header,struct image_t* image, struct fm_t* fp, int want_rgba);
static int tga_put_packet_tuples(int* current_byte,struct tga_header_t* tga_header,struct image_t* image,unsigned char* temp_colour, int want_rgba);

/*
 * public entry point for .tga reader
 */

int image_sread_tga(const char* data, unsigned len, struct image_t* destination_image, int want_rgba)
{
	struct fm_t fm;
	struct tga_header_t header;

	image_free(destination_image);
	fm_open_mem((char*)data,len,&fm);

	if (tga_read_header(&fm,&header) != 0) return 1;

	
	switch (header.image_type)
	{
		case 2: /* RGB(A) uncompressed */
		case 10: /* RGB(A) compressed */
			switch (header.pixel_depth)
			{
				case 24:
					destination_image->num_channels = 3;
					break;
				case 32: 
					destination_image->num_channels = 4;
					break;
				default:
					fm_close(&fm);
					return 2;
			}
			break;

		case 3: /* Greyscale uncompressed */
			destination_image->num_channels = 1;
			break;

		default:
			fm_close(&fm);
			return 2; /* unsupported image type */
	}

	destination_image->width = header.width;
	destination_image->height = header.height;
	destination_image->num_mipmaps = 0;
	destination_image->bytes_per_pixel = header.pixel_depth;

	if (tga_read_image_data(&fm,&header,destination_image,want_rgba) != 0) {
		fm_close(&fm);
		image_free(destination_image);
		return 1;
	}

	fm_close(&fm);
	return 0;
}

static int tga_read_header(struct fm_t* fp, struct tga_header_t* tga_header)
{
	if (fm_read(&tga_header->id_length,	      sizeof(uint8_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->colour_map_type, sizeof(uint8_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->image_type,      sizeof(uint8_t), 1, fp) != 1) return 1;

	if (fm_read(&tga_header->colour_map_first_entry, sizeof(uint16_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->colour_map_length,      sizeof(uint16_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->colour_map_entry_size,  sizeof(uint8_t), 1, fp) != 1) return 1;

	if (fm_read(&tga_header->x_origin, sizeof(uint16_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->y_origin, sizeof(uint16_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->width,    sizeof(uint16_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->height,   sizeof(uint16_t), 1, fp) != 1) return 1;

	if (fm_read(&tga_header->pixel_depth,      sizeof(uint8_t), 1, fp) != 1) return 1;
	if (fm_read(&tga_header->image_descriptor, sizeof(uint8_t), 1, fp) != 1) return 1;

	/* Set some of the stats */
	tga_header->components = tga_header->pixel_depth / 8;
	tga_header->bytes      = tga_header->width * tga_header->height * tga_header->components;

	return 0;
}

static int tga_read_image_data(struct fm_t* fp, struct tga_header_t* tga_header, struct image_t* image, int want_rgba)
{
	/* allocate image memory */
	image->pixels = malloc(tga_header->bytes);
	if (image->pixels == 0) return 1;
	memset(image->pixels,0,tga_header->bytes);

	switch (tga_header->image_type)
	{
		case 2: /* easy uncompressed image */
		case 3:
			fm_read(image->pixels,1,tga_header->bytes,fp);

			/* Image is stored as BGR(A), make it RGB(A) if needed */
			if (tga_header->components == 3 || tga_header->components == 4)
			{ 
				if (want_rgba)
				{
					int i;
					uint8_t *p = image->pixels;

					for (i = 0; i < tga_header->bytes; i += tga_header->components) {
						uint8_t temp = p[i];
						p[i] = p[i + 2];
						p[i + 2] = temp;
					}
				}
			}
			break;

		case 9: /* compressed image */
		case 10:
			{
				int current_byte = 0;

				while (current_byte < tga_header->bytes) {
					tga_get_packet(&current_byte,tga_header,image,fp,want_rgba);
				}
			}
			break;
	}

	return 0;
}

static int tga_get_packet(int* current_byte,struct tga_header_t* tga_header,struct image_t* image, struct fm_t* fp, int want_rgba)
{
	unsigned char  packet_header, *p=image->pixels;
	int            run_length, i;
	unsigned char  temp_colour[4] = {0, 0, 0, 0};

	fm_read(&packet_header,1,1,fp);
	run_length = (packet_header & 0x7F) + 1;

	if (packet_header & 0x80)  // RLE packet
	{
		fm_read(temp_colour, tga_header->components, 1, fp);

		if (tga_header->components == 1)   // Special optimised case
		{
			memset (p + (*current_byte),temp_colour[0],run_length);
			(*current_byte) += run_length;
		} 
		else {
			for (i = 0; i < run_length; i++)
				tga_put_packet_tuples(current_byte,tga_header,image,temp_colour,want_rgba);
		}
	}

	if (!(packet_header & 0x80))  // RAW packet
	{
		for (i = 0; i < run_length; i++)
		{
			fm_read(temp_colour, tga_header->components, 1, fp);

			if (tga_header->components == 1)
			{
				memset(p + (*current_byte), temp_colour[0], run_length);
				(*current_byte) += run_length;
			} 
			else
				tga_put_packet_tuples(current_byte,tga_header,image,temp_colour,want_rgba);
		}
	}

	return 0;
}

static int tga_put_packet_tuples(int* current_byte,struct tga_header_t* tga_header,struct image_t* image,unsigned char* temp_colour, int want_rgba)
{
	unsigned char* p = image->pixels;

	if (!want_rgba)
	{
		if (tga_header->components == 3)
		{
			p[*current_byte]     = temp_colour[0];
			p[(*current_byte)+1] = temp_colour[1];
			p[(*current_byte)+2] = temp_colour[2];
			(*current_byte) += 3;
		}

		if (tga_header->components == 4)
		{
			p[*current_byte]     = temp_colour[0];
			p[(*current_byte)+1] = temp_colour[1];
			p[(*current_byte)+2] = temp_colour[2];
			p[(*current_byte)+3] = temp_colour[3];
			(*current_byte) += 4;
		}
	}
	else /* RGBA || RGB */
	{
		if (tga_header->components == 3)
		{
			p[*current_byte]     = temp_colour[2];
			p[(*current_byte)+1] = temp_colour[1];
			p[(*current_byte)+2] = temp_colour[0];
			(*current_byte) += 3;
		}

		if (tga_header->components == 4)
		{
			p[*current_byte]     = temp_colour[2];
			p[(*current_byte)+1] = temp_colour[1];
			p[(*current_byte)+2] = temp_colour[0];
			p[(*current_byte)+3] = temp_colour[3];
			(*current_byte) += 4;
		}
	}

	return 0;
}



